import { accountService } from '@/services/security/LoginAccount';
import { HttpUtil } from '@/utils/HttpUtil';
import { DateUtil } from '@/utils/DateUtil';
import defaultSettings from '@/settings';
import { Info } from '@/store/modules/account/info';
import { ActionContext, Module } from 'vuex';
import { State } from '@/store';
import {
  info,
  localValidate,
  login,
  logout,
  remoteValidate,
  RESET_STATE,
  SET_INFO,
  SET_STATE,
  SET_TOKEN,
  token,
  upgrade,
} from '@/store/modules/account/pool';
import { connect, disconnect } from '@/store/modules/stomp/pool';
import { filterAsyncRoutes, resetRoutes } from '@/store/modules/routes/pool';
import jwtDecode, { JwtPayload } from 'jwt-decode';
import { router } from '@/router';

export type JWT = Required<JwtPayload> & {
  account: Info;
};

const formatAccountInfo = (info?: Info) => {
  if (!info) {
    info = {} as Info;
  }
  if (!info.avatar) {
    info.avatar = '/api' + defaultSettings.defaultAvatar;
  } else {
    info.avatar = '/api' + info.avatar;
  }
  if (!info.roles) {
    info.roles = [];
  }
  if (!info.accesses) {
    info.accesses = [];
  }
  if (!info.authorities) {
    info.authorities = [];
  }
  if (!info.time) {
    info.time = DateUtil.format(new Date());
  }
  return info;
};

export interface AccountState {
  token: string | null;
  info: Info;
}

export const account: Module<AccountState, State> = {
  namespaced: true,
  state: () => ({
    token: null,
    info: formatAccountInfo(),
  }),
  getters: {
    [token]: state => state.token,
    [info]: state => state.info,
  },
  mutations: {
    [RESET_STATE]: state => {
      Object.assign(state, {
        token: null,
        info: formatAccountInfo(),
      });
    },
    [SET_STATE]: (state, newState) => {
      Object.assign(state, newState);
    },
    [SET_TOKEN]: (state, token: string) => {
      state.token = token;
    },
    [SET_INFO]: (state, info: Info) => {
      // console.log(state);
      state.info = info;
      // console.log(state);
    },
  },
  actions: {
    async [login]({ commit, dispatch }: ActionContext<AccountState, State>, account) {
      const resp = await accountService.login(account);
      const token = HttpUtil.getToken(resp);
      commit(SET_TOKEN, token);
      const info = formatAccountInfo(HttpUtil.getData(resp));
      await dispatch(`routes/${filterAsyncRoutes}`, info, { root: true });
      commit(SET_INFO, info);
      if (defaultSettings.websocket) {
        await dispatch(`stomp/${connect}`, { Authorization: token }, { root: true });
      }
      return info;
    },
    async [logout]({ commit, dispatch }: ActionContext<AccountState, State>, authorized?: boolean) {
      if (authorized || authorized === undefined) {
        await accountService.logout();
      }
      await dispatch(`routes/${resetRoutes}`, null, { root: true });
      commit(RESET_STATE);
      if (defaultSettings.websocket) {
        await dispatch(`stomp/${disconnect}`, null, { root: true });
      }
    },
    async [localValidate]({ state, dispatch }, callback?: () => void) {
      if (state.token) {
        const claims = jwtDecode<JWT>(state.token);
        // console.log(claims);
        if (new Date(1000 * claims.exp) <= new Date()) {
          if (callback) {
            callback();
          } else {
            dispatch(logout, false).then(() => {
              return router.push('/login');
            });
          }
          return false;
        } else {
          return true;
        }
      }
    },
    async [remoteValidate]({ commit, dispatch }, callback?: () => void) {
      if (await dispatch(localValidate)) {
        try {
          const resp = await accountService.information();
          if (HttpUtil.isOk(resp)) {
            const info = formatAccountInfo(HttpUtil.getData(resp));
            commit(SET_INFO, info);
            return true;
          }
        } catch (e) {
          if (callback) {
            callback();
          } else {
            dispatch(logout, false).then(() => {
              return router.push('/login');
            });
          }
          return false;
        }
      } else {
        return false;
      }
    },
    async [upgrade]({ commit }) {
      const resp = await accountService.upgradeInfo();
      commit(SET_TOKEN, HttpUtil.getToken(resp));
      const info = formatAccountInfo(HttpUtil.getData(resp));
      commit(SET_INFO, info);
      return info;
    },
  },
};
